home *** CD-ROM | disk | FTP | other *** search
- /* Modified by Lal.George@att.com for use in the SML/NJ compiler */
-
- /* Modified by Andrew.Vignaux@comp.vuw.ac.nz to get it to work :-) */
-
- /* Copyright (C) 1985, 1986, 1987, 1988 Free Software Foundation, Inc.
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 1, or (at your option)
- any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-
- In other words, you are welcome to use, share and improve this program.
- You are forbidden to forbid anyone else to use, share and improve
- what you give them. Help stamp out software-hoarding! */
-
- /*
- * unexec.c - Convert a running program into an a.out file.
- *
- * Author: Spencer W. Thomas
- * Computer Science Dept.
- * University of Utah
- * Date: Tue Mar 2 1982
- * Modified heavily since then.
- *
- * Synopsis:
- * unexec (new_name, a_name, data_start, bss_start, entry_address)
- * char *new_name, *a_name;
- * unsigned data_start, bss_start, entry_address;
- *
- * Takes a snapshot of the program and makes an a.out format file in the
- * file named by the string argument new_name.
- * If a_name is non-NULL, the symbol table will be taken from the given file.
- * On some machines, an existing a_name file is required.
- *
- * The boundaries within the a.out file may be adjusted with the data_start
- * and bss_start arguments. Either or both may be given as 0 for defaults.
- *
- * Data_start gives the boundary between the text segment and the data
- * segment of the program. The text segment can contain shared, read-only
- * program code and literal data, while the data segment is always unshared
- * and unprotected. Data_start gives the lowest unprotected address.
- * The value you specify may be rounded down to a suitable boundary
- * as required by the machine you are using.
- *
- * Specifying zero for data_start means the boundary between text and data
- * should not be the same as when the program was loaded.
- * If NO_REMAP is defined, the argument data_start is ignored and the
- * segment boundaries are never changed.
- *
- * Bss_start indicates how much of the data segment is to be saved in the
- * a.out file and restored when the program is executed. It gives the lowest
- * unsaved address, and is rounded up to a page boundary. The default when 0
- * is given assumes that the entire data segment is to be stored, including
- * the previous data and bss as well as any additional storage allocated with
- * break (2).
- *
- * The new file is set up to start at entry_address.
- */
-
- #include <a.out.h>
- #include <errno.h>
- #include <stdio.h>
- #include <sys/stat.h>
-
- #define PERROR(arg) {perror(arg); return -1;}
-
- extern int _data;
- extern int _edata;
- extern int _text;
- extern int _etext;
- extern int _end;
-
-
- static long block_copy_start;
- static struct filehdr f_hdr;
- static struct aouthdr f_ohdr;
- long bias; /* bias to add for growth */
- long lnnoptr; /* pointer to line no. info */
- #define SYMS_START block_copy_start
-
- static long text_scnptr;
- static long data_scnptr;
-
- static long load_scnptr;
- static long orig_load_scnptr;
- static long orig_data_scnptr;
-
- #define ADDR_CORRECT(x) ((int)(x))
- #define MAX_SECTIONS 16
- static int pagemask;
-
-
- extern char ** global_argv;
- extern int old_high;
-
-
- static char * start_of_text()
- {
- return ((char *) 0x10000000);
- }
-
-
- static char * start_of_data ()
- {
- return ((char *) 0x20000000);
- }
-
-
- static report_error (file, fd)
- char *file;
- int fd;
- {
- if (fd) close (fd);
- chatting ("Failure operating on %s", file);
- }
-
-
-
- static report_error_1 (fd, msg, a1, a2)
- int fd;
- char *msg;
- int a1, a2;
- {
- close (fd);
- chatting (msg, a1, a2);
- }
-
-
-
- int sys_write (fildes, buf, nbyte)
- int fildes;
- char *buf;
- unsigned int nbyte;
- {
- register int rtnval;
-
- while ((rtnval = write (fildes, buf, nbyte)) == -1
- && (errno == 4));
- return (rtnval);
- }
-
-
- unexec (new, a_name, data_start, bss_start, entry_address)
- int new;
- char *a_name;
- unsigned data_start, bss_start, entry_address;
- {
- int a_out = -1;
-
- if (a_name && (a_out = open (a_name, 0)) < 0){ PERROR (a_name); }
-
- if (make_hdr (new,a_out,data_start,bss_start,entry_address,a_name)<0 ||
- copy_text_and_data (new) < 0 ||
- copy_sym (new, a_out, a_name) < 0 ||
- adjust_lnnoptrs (new, a_out) < 0 ||
- unrelocate_symbols (new, a_out, a_name) < 0)
- {
- return -1;
- }
-
- if (a_out >= 0) close (a_out);
- fchmod(new,0755);
- return 0;
- }
-
-
-
- #define CHECK_SCNHDR(ptr, name, flags) \
- if (strcmp(s->s_name, name) == 0) { \
- if (s->s_flags != flags) { \
- chatting("unexec: %x flags where %x expected in %s section.\n", \
- s->s_flags, flags, name); \
- } \
- if (ptr) { \
- chatting("unexec: duplicate section header for section %s.\n", \
- name); \
- } \
- ptr = s; \
- }
-
- static int make_hdr (new, a_out, data_start, bss_start,
- entry_address, a_name)
- int new, a_out;
- unsigned data_start, bss_start, entry_address;
- char *a_name;
- #define ERROR0(msg) report_error_1 (new, msg, 0, 0); return -1
- #define ERROR1(msg,x) report_error_1 (new, msg, x, 0); return -1
- #define ERROR2(msg,x,y) report_error_1 (new, msg, x, y); return -1
- {
- register int scns;
- unsigned int bss_end;
-
- struct scnhdr section[MAX_SECTIONS];
- struct scnhdr * f_thdr; /* Text section header */
- struct scnhdr * f_dhdr; /* Data section header */
- struct scnhdr * f_bhdr; /* Bss section header */
- struct scnhdr * f_lhdr; /* Loader section header */
- struct scnhdr * f_tchdr; /* Typechk section header */
- struct scnhdr * f_dbhdr; /* Debug section header */
- struct scnhdr * f_xhdr; /* Except section header */
-
- load_scnptr = orig_load_scnptr = lnnoptr = 0;
-
- pagemask = getpagesize () - 1;
-
- /* Adjust text/data boundary. */
- data_start = (long) start_of_data ();
- data_start = ADDR_CORRECT (data_start);
-
- data_start = data_start & ~pagemask;
-
- /*
- ** bss_end = ADDR_CORRECT (sbrk (0)) + pagemask;
- */
- bss_end = ADDR_CORRECT (old_high) + pagemask;
- bss_end &= ~ pagemask;
-
- /* Adjust data/bss boundary. */
- if (bss_start != 0)
- {
- bss_start = (ADDR_CORRECT (bss_start) + pagemask);
- /* (Up) to page bdry. */
- bss_start &= ~ pagemask;
- if (bss_start > bss_end)
- {
- ERROR1("unexec: bss_start (%u) is past end of program",
- bss_start);
- }
- }
- else
- bss_start = bss_end;
-
- if (data_start > bss_start) /* Can't have negative data size. */
- {
- ERROR2 ("unexec: data_start (%u) > bss_start (%u)",
- data_start, bss_start);
- }
-
- /* Salvage as much info from the existing file as possible */
- block_copy_start = 0;
- f_thdr = NULL; f_dhdr = NULL; f_bhdr = NULL;
- f_lhdr = NULL; f_tchdr = NULL; f_dbhdr = NULL; f_xhdr = NULL;
-
- if (a_out >= 0)
- {
- if (read (a_out, &f_hdr, sizeof (f_hdr)) != sizeof (f_hdr))
- {
- PERROR (a_name);
- }
- block_copy_start += sizeof (f_hdr);
-
- if (f_hdr.f_opthdr > 0)
- {
- if (read (a_out, &f_ohdr, sizeof (f_ohdr)) !=
- sizeof (f_ohdr))
- {
- PERROR (a_name);
- }
- block_copy_start += sizeof (f_ohdr);
- }
- if (f_hdr.f_nscns > MAX_SECTIONS)
- {
- ERROR0 ("unexec: too many section headers");
- }
- /* Loop through section headers */
- for (scns = 0; scns < f_hdr.f_nscns; scns++)
- {
- struct scnhdr *s = §ion[scns];
- if (read (a_out, s, sizeof (*s)) != sizeof (*s))
- {
- PERROR (a_name);
- }
- if (s->s_scnptr > 0L)
- {
- if (block_copy_start < s->s_scnptr + s->s_size)
- block_copy_start = s->s_scnptr + s->s_size;
- }
-
- CHECK_SCNHDR(f_thdr, _TEXT, STYP_TEXT);
- CHECK_SCNHDR(f_dhdr, _DATA, STYP_DATA);
- CHECK_SCNHDR(f_bhdr, _BSS, STYP_BSS);
- CHECK_SCNHDR(f_lhdr, _LOADER, STYP_LOADER);
- CHECK_SCNHDR(f_dbhdr, _DEBUG, STYP_DEBUG);
- CHECK_SCNHDR(f_tchdr, _TYPCHK, STYP_TYPCHK);
- CHECK_SCNHDR(f_xhdr, _EXCEPT, STYP_EXCEPT);
- }
-
- if (f_thdr == 0)
- {
- ERROR1 ("unexec: couldn't find \"%s\" section", _TEXT);
- }
- if (f_dhdr == 0)
- {
- ERROR1 ("unexec: couldn't find \"%s\" section", _DATA);
- }
- if (f_bhdr == 0)
- {
- ERROR1 ("unexec: couldn't find \"%s\" section", _BSS);
- }
- }
- else
- {
- ERROR0 ("can't build a COFF file from scratch yet");
- }
-
- orig_data_scnptr = f_dhdr->s_scnptr;
- orig_load_scnptr = f_lhdr ? f_lhdr->s_scnptr : 0;
-
- /* Now we alter the contents of all the f_*hdr variables
- ** to correspond to what we want to dump.
- */
- f_hdr.f_flags |= (F_RELFLG | F_EXEC); /* Why? */
-
- f_ohdr.dsize = bss_start - ((unsigned) &_data);
- f_ohdr.bsize = bss_end - bss_start;
-
- f_dhdr->s_size = f_ohdr.dsize;
- f_bhdr->s_size = f_ohdr.bsize;
- f_bhdr->s_paddr = f_ohdr.dsize;
- f_bhdr->s_vaddr = f_ohdr.dsize;
-
- /* fix scnptr's */
- {
- long ptr;
-
- for (scns = 0; scns < f_hdr.f_nscns; scns++)
- {
- struct scnhdr *s = §ion[scns];
- if (scns == 0)
- ptr = s->s_scnptr;
-
- if (s->s_scnptr != 0)
- {
- s->s_scnptr = ptr;
- }
-
- if ((s->s_flags & 0xffff) == STYP_PAD)
- {
- /*
- ** the text_start should probably be o_algntext
- ** but that doesn't seem to change
- */
- if (f_ohdr.text_start != 0) /* && scns != 0 */
- {
- s->s_size = 512 - (s->s_scnptr % 512);
- if (s->s_size == 512)
- s->s_size = 0;
- }
- }
-
- ptr = ptr + s->s_size;
- }
-
- bias = ptr - block_copy_start;
- }
-
- /* fix other pointers */
- for (scns = 0; scns < f_hdr.f_nscns; scns++)
- {
- struct scnhdr *s = §ion[scns];
-
- if (s->s_relptr != 0)
- {
- s->s_relptr += bias;
- }
- if (s->s_lnnoptr != 0)
- {
- if (lnnoptr == 0) lnnoptr = s->s_lnnoptr;
- s->s_lnnoptr += bias;
- }
- }
-
- if (f_hdr.f_symptr > 0L)
- {
- f_hdr.f_symptr += bias;
- }
-
- text_scnptr = f_thdr->s_scnptr;
- data_scnptr = f_dhdr->s_scnptr;
- load_scnptr = f_lhdr ? f_lhdr->s_scnptr : 0;
- block_copy_start = orig_load_scnptr;
-
- if (write (new, &f_hdr, sizeof (f_hdr)) != sizeof (f_hdr))
- {
-
- }
-
- if (f_hdr.f_opthdr > 0)
- {
- if (write (new, &f_ohdr, sizeof (f_ohdr)) != sizeof (f_ohdr))
- {
- report_error("reading filehdr", new);
- return -1;
- }
- }
-
- for (scns = 0; scns < f_hdr.f_nscns; scns++)
- {
- struct scnhdr *s = §ion[scns];
- if (write (new, s, sizeof (*s)) != sizeof (*s))
- {
- report_error("reading section header",new);
- return -1;
- }
- }
-
- return (0);
- }
-
-
- #define write_segment(fd,start,end) bulletproofWrite0(fd,start,end-start)
-
- static int copy_text_and_data (new)
- int new;
- {
- register char *end;
- register char *ptr;
-
- lseek (new, (long) text_scnptr, 0);
- ptr = start_of_text () + text_scnptr;
- end = ptr + f_ohdr.tsize;
- write_segment (new, ptr, end);
-
- lseek (new, (long) data_scnptr, 0);
- ptr = (char *) &_data;
- end = ptr + f_ohdr.dsize;
- write_segment (new, ptr, end);
-
- return 0;
- }
-
-
-
- static int copy_sym (new, a_out, a_name)
- int new, a_out;
- char *a_name;
- {
- char page[1024];
- int n;
-
- if (a_out < 0) return 0;
-
- if (SYMS_START == 0L) return 0;
-
- if (lnnoptr && lnnoptr < SYMS_START)
- lseek (a_out, lnnoptr, 0); /* start copying from there */
- else
- lseek (a_out, SYMS_START, 0); /* Position a.out to symtab. */
-
- while ((n = read (a_out, page, sizeof page)) > 0)
- {
- if (write (new, page, n) != n)
- {
- chatting("copy_sym: Error in reading a.out\n");
- return -1;
- }
- if (n < 0)
- {
- chatting("copy_sym: cannot write -ve number of bytes\n");
- return -1;
- }
- }
- return 0;
- }
-
-
- adjust_lnnoptrs (writedesc, readdesc)
- int writedesc;
- int readdesc;
- {
- register int nsyms;
- register int new = writedesc;
- struct syment symentry;
- union auxent auxentry;
-
- if (!lnnoptr || !f_hdr.f_symptr) return 0;
-
- lseek (new, f_hdr.f_symptr, 0);
- for (nsyms = 0; nsyms < f_hdr.f_nsyms; nsyms++)
- {
- read (new, &symentry, SYMESZ);
- if (symentry.n_numaux)
- {
- read (new, &auxentry, AUXESZ);
- nsyms++;
- if (ISFCN (symentry.n_type))
- {
- auxentry.x_sym.x_fcnary.x_fcn.x_lnnoptr += bias;
- lseek (new, -AUXESZ, 1);
- write (new, &auxentry, AUXESZ);
- }
- }
- }
- }
-
- unrelocate_symbols (new, a_out, a_name)
- int new, a_out;
- char *a_name;
- {
- register int i;
- register int l;
- register LDREL *ldrel;
- LDHDR ldhdr;
- LDREL ldrel_buf [20];
- ulong t_start = &_text;
- ulong d_start = &_data;
- int * p;
- int dirty;
-
- if (load_scnptr == 0) return 0;
-
- lseek (a_out, orig_load_scnptr, 0);
- if (read (a_out, &ldhdr, sizeof (ldhdr)) != sizeof (ldhdr))
- {
- chatting("Could not read loader section header\n");
- return -1;
- }
-
- #define SYMNDX_TEXT 0
- #define SYMNDX_DATA 1
- #define SYMNDX_BSS 2
- l = 0;
- for (i = 0; i < ldhdr.l_nreloc; i++, l--, ldrel++)
- {
- if (l == 0)
- {
- lseek (a_out,
- orig_load_scnptr+LDHDRSZ+LDSYMSZ*ldhdr.l_nsyms
- + LDRELSZ*i,
- 0);
-
- l = ldhdr.l_nreloc - i;
- if (l > sizeof (ldrel_buf) / LDRELSZ)
- l = sizeof (ldrel_buf) / LDRELSZ;
-
- if (read (a_out, ldrel_buf, l * LDRELSZ) != l * LDRELSZ)
- {
- PERROR (a_name);
- }
- ldrel = ldrel_buf;
- }
- dirty = 0;
-
- /* this code may not be necessary */
- /* I originally had == in the "assignment" and it
- still unrelocated */
-
- /* move the BSS loader symbols to the DATA segment */
- if (ldrel->l_rsecnm == f_ohdr.o_snbss)
- ldrel->l_rsecnm = f_ohdr.o_sndata, dirty++;
-
- if (ldrel->l_symndx == SYMNDX_BSS)
- ldrel->l_symndx = SYMNDX_DATA, dirty++;
-
- if (dirty)
- {
- lseek (new,
- load_scnptr + LDHDRSZ + LDSYMSZ*ldhdr.l_nsyms + LDRELSZ*i,
- 0);
-
- if (write (new, ldrel, LDRELSZ) != LDRELSZ)
- {
- chatting("could not write to output file\n");
- return -1;
- }
- }
-
- if (ldrel->l_rsecnm == f_ohdr.o_sndata)
- {
- int orig_int;
-
- lseek (a_out, orig_data_scnptr + ldrel->l_vaddr, 0);
-
- if (read (a_out, (void *) &orig_int, sizeof (orig_int))
- != sizeof (orig_int))
- {
- PERROR (a_name);
- }
-
- switch (ldrel->l_symndx)
- {
- case SYMNDX_TEXT:
- p = (int *) (d_start + ldrel->l_vaddr);
- orig_int = * p - (t_start - f_ohdr.text_start);
- break;
-
- case SYMNDX_DATA:
- case SYMNDX_BSS:
- p = (int *) (d_start + ldrel->l_vaddr);
- orig_int = * p - (d_start - f_ohdr.data_start);
- break;
- }
-
- lseek (new, data_scnptr + ldrel->l_vaddr, 0);
- if (write (new, (void *) &orig_int, sizeof (orig_int)) !=
- sizeof (orig_int))
- {
- chatting("could not write to output file\n");
- return -1;
- }
- }
- }
- }
-
-
-
- #define MAXPATHLEN 512
- #define isabsolutePath(name) (name[0] == '/')
-
-
- hasSlash (name) char * name;
- {
- int n = strlen(name);
- int i;
-
- for (i = 0; i<n; i++)
- if (name[i] == '/') return 1;
-
- return 0;
- }
-
-
- /*
- ** searchPath (name) -- search PATH environment variable
- ** - modifies name
- * - returns 1 on success and 0 on failure.
- */
- searchPath(name) char * name;
- {
- char * path;
- int n;
- char fileName[256];
-
- int i,j;
-
- path = getenv("PATH");
- n = strlen (path);
-
- j = 0;
- for (i=0; i < n; i++)
- {
- if (path[i] != ':') fileName[j++] = path[i];
- else
- {
- struct stat statBuff;
-
- fileName[j] = '/';
- fileName[j+1] = '\0';
- strcat(fileName,name);
- if (stat(fileName, & statBuff) == 0)
- {
- strcpy(name,fileName);
- return 1;
- }
- else j = 0;
- }
- }
- return 0;
- }
-
-
- baseName (name) char * name;
- {
- int i, n = strlen(name);
-
- for (i=n; i>=0 && name[i] != '/'; i--)
- name[i] = '\0';
- }
-
- /*
- ** chaseLinks (buff) -- chases links until the executable is found.
- ** - modifies buff
- ** - returns 1 on success and 0 on failure.
- ** ToDo: - Should probably check that the file returned is executable
- */
- char * chaseLinks(buff) char buff[];
- {
- int n;
- char ans[MAXPATHLEN];
-
- while (1)
- {
- if ((n=readlink(buff,ans,MAXPATHLEN-1)) == -1)
- {
- switch (errno)
- {
- case EINVAL: return 1;
- case ENOENT: chatting("chaseLinks: invalid file\n");
- return 0;
- default: chatting("chaseLinks: impossible\n");
- return 0;
- }
- }
- else
- ans[n] = '\0';
- if (isabsolutePath(ans))
- {
- strncpy(buff,ans,n+1);
- }
- else
- {
- baseName(buff);
- if (strlen(buff) + strlen(ans) >= MAXPATHLEN)
- {
- chatting("chaseLinks: path name too long\n");
- return 0;
- }
- strncat(buff,ans,n);
- }
- }
- }
-
-
- getaoutname(name) char *name;
- {
- if (!(isabsolutePath(name) || hasSlash(name)))
- if (searchPath(name) == 0)
- {
- chatting("chaseLinks: Could not find executable in path!\n");
- return 0;
- }
- return(chaseLinks(name));
- }
-
-
-
-
- export(filid)
- int filid;
- {
- char buff[MAXPATHLEN];
-
- strcpy(buff,global_argv[0]);
-
- if (getaoutname(buff) == 0)
- {
- chatting("export: getaoutname failed\n");
- return 1;
- }
-
- if (unexec(filid,buff,0,0,0) == 0) return 0;
- else
- {
- chatting ("export: Unexec failed\n");
- return 1;
- }
- }
-
-